package fnic.modesearch;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;

import org.apache.log4j.Logger;
import org.codehaus.jackson.JsonEncoding;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.get.MultiGetItemResponse;
import org.elasticsearch.action.get.MultiGetResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.geo.GeoDistance;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.AndFilterBuilder;
import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.FilterBuilders;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.node.Node;
import org.elasticsearch.node.NodeBuilder;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.facet.FacetBuilder;
import org.elasticsearch.search.facet.FacetBuilders;
import org.elasticsearch.search.facet.range.RangeFacet;
import org.elasticsearch.search.facet.statistical.StatisticalFacet;
import org.elasticsearch.search.facet.terms.TermsFacet;
import org.elasticsearch.search.facet.terms.TermsFacet.ComparatorType;

import fnic.jsontrans.interfacer.PhnFriendsTrace;
import fnic.jsontrans.interfacer.PhnUserTrace;
import fnic.jsontrans.modesearch.RangeFacetOutput;
import fnic.jsontrans.modesearch.StaticalFacetOutput;
import fnic.jsontrans.modesearch.TermFacetOutput;
import fnic.prehand.action.SearchFromESServlet;
import fnic.prehand.common.AgentConfig;
import fnic.prehand.common.JacksonFormatService;
import fnic.prehand.esagent.EsAgent;

public class SimpleIFSearch extends EsAgent{
	private static final Logger LOG = Logger.getLogger(SimpleIFSearch.class);
	private static final AgentConfig config = AgentConfig.getInstance();
	private static final JacksonFormatService jsonInstance = JacksonFormatService.getInstance();
	private static final String clustername = config.getEsClusterName();
	private String indexName;
	private String idxtypeName;
	
	HashSet<String> hashSet = null; 
	JsonGenerator outputGenerator = null;
	ByteArrayOutputStream out = null;
	
	public SimpleIFSearch(String idx, String type){
		indexName = idx;
		idxtypeName = type;
		hashSet = new HashSet<String>();
		out = new ByteArrayOutputStream();
		JsonFactory f = new JsonFactory();
		try {
			outputGenerator = f.createJsonGenerator(out, JsonEncoding.UTF8);
		} catch (IOException e) {
			LOG.error(e.getMessage());
		}
	}



	public String idGet(String id) {
		String resultSrcJson = "{\"id\":\"" + id
				+ "\",\"result\":\"no match!\"}";
		LOG.info("do get index: " + id + "........");
		GetResponse response = null;
		try {
			response = client.prepareGet(indexName, idxtypeName, id).execute()
					.actionGet();
		}  catch (Exception e) {
			LOG.error(e.getMessage());
		}

		if (response != null && response.isExists()) {
			// 索引名称
			String _index = response.getIndex();
			// type名称
			String _type = response.getType();
			// 文档ID
			String _id = response.getId();
			// 索引版本
			long _version = response.getVersion();

			resultSrcJson = response.getSourceAsString();

			LOG.debug("search result index:" + _index + "  type:" + _type
					+ "  id:" + _id + "  version:" + _version + "  source: "
					+ resultSrcJson);
			LOG.info("get index "+_index+"succeed!");

		} else {
			LOG.error(resultSrcJson);
		}
		return resultSrcJson;
	}

	// 获取所有ids对应的源数据
	public String[] getMtlId(String[] ids, int size) {
		int num = 0;
		String[] jsonDocs = new String[size];

		LOG.info("do get mutli index size: " + size + "........");
		MultiGetResponse multiRsp = null;

		try {
			multiRsp = client.prepareMultiGet().add(indexName, idxtypeName, ids).execute().actionGet();
		} catch (Exception e) {
			LOG.error(e.getMessage());
			return null;
		}

		if (multiRsp!=null) {
			Iterator<MultiGetItemResponse> Itor = multiRsp.iterator();

			while (Itor.hasNext()) {
				MultiGetItemResponse itemRsp = Itor.next();
				if (!itemRsp.isFailed() && itemRsp.getResponse().isExists()) {
					jsonDocs[num] = itemRsp.getResponse().getSourceAsString();
				} else {
					// jsonDocs[num] = itemRsp.getFailure().toString();
					jsonDocs[num] = "{\"id\":\"" + ids[num]
							+ "\",\"result\":\"no match!\"}";
					LOG.error("ids[" + num + "] not exist.");
				}
				num++;
			}
		}
		LOG.info("result Rsp size: " + num + "........");
		return jsonDocs;
	}

	public RangeFacetOutput rangeFacet(double curPingDelay,
			String pingDelayField) {
		
		FacetBuilder range = FacetBuilders.rangeFacet("myfacet")
				.field(pingDelayField) // Field to compute on
				.addUnboundedFrom(curPingDelay) // from -infinity to 3
												// (excluded)
				// .addRange(3, 6) // from 3 to 6 (excluded)
				.addUnboundedTo(curPingDelay); // from 6 to +infinity

		SearchResponse response = client.prepareSearch(indexName)
				.setTypes(idxtypeName)
				// .setSize(Integer.MAX_VALUE) //不设置 ，默认返回10个
				.setQuery(QueryBuilders.termQuery("probetype", 2))
				.addFacet(range).execute().actionGet();
		
		if(response.status() == RestStatus.OK){
			RangeFacetOutput rangeFacet = new RangeFacetOutput();
			// sr is here your SearchResponse object
			RangeFacet f = (RangeFacet) response.getFacets().facetsAsMap()
					.get("myfacet");
			int i = 0;
			long allcount = 0;
			// For each entry
			for (RangeFacet.Entry entry : f) {
				double from = entry.getFrom(); // Range from requested
				double to = entry.getTo(); // Range to requested
				long count = entry.getCount(); // Doc count
				double min = entry.getMin(); // Min value
				double max = entry.getMax(); // Max value
				double mean = entry.getMean(); // Mean
				double total = entry.getTotal(); // Sum of values

				LOG.info("from:" + from + ",  to:" + to + ",  count:" + count
						+ ",  min:" + min + ",  max:" + max + ",  mean:" + mean
						+ ",  total:" + total);
				rangeFacet.addRangeItem(new RangeFacetOutput.RangeInfo(from, to,
						count, min, max, mean, total));
				i++;
				allcount += count;
			}
			return rangeFacet;
		}else{
			//ES检索失败--field分段数学统计失败
			return null;
		}
	}

	// 统计某字段的平均值
	public StaticalFacetOutput staticalFacet(String fieldName) {
		FacetBuilder statical = FacetBuilders.statisticalFacet("f").field(
				fieldName);

		SearchResponse response = client.prepareSearch(indexName)
				.setTypes(idxtypeName)
				.setQuery(QueryBuilders.termQuery("probetype", 2))
				.addFacet(statical).execute().actionGet();

		if(response.status() == RestStatus.OK){
			// sr is here your SearchResponse object
			StatisticalFacet f = (StatisticalFacet) response.getFacets()
					.facetsAsMap().get("f");

			long count = f.getCount(); // Doc count
			double min = f.getMin(); // Min value
			double max = f.getMax(); // Max value
			double mean = f.getMean(); // Mean
			double total = f.getTotal(); // Sum of values
			double stddev = f.getStdDeviation(); // Standard Deviation
			double sumsqr = f.getSumOfSquares(); // Sum of Squares
			double var = f.getVariance(); // Variance
			LOG.info("count:" + count + ",  min:" + min + ",  max:" + max
					+ ",  mean:" + mean + ",  total:" + total);

			StaticalFacetOutput staticalfacet = new StaticalFacetOutput(count, min,
					max, mean, total, stddev, sumsqr, var);
			return staticalfacet;
		}else{
			//ES检索失败--field全局数学统计失败
			return null;
		}

	}

	//
	public TermFacetOutput termFacet(String fieldName) {
		FacetBuilder fb = FacetBuilders.termsFacet("subjectFacet")
				.field(fieldName).order(ComparatorType.TERM);

		SearchResponse response = client.prepareSearch(indexName)
				.setTypes(idxtypeName)
				// .setSize(Integer.MAX_VALUE) //不设置，默认返回10个
				.setQuery(QueryBuilders.termQuery("probetype", 2)).addFacet(fb)
				.execute().actionGet();

		if(response.status() == RestStatus.OK){
			// sr is here your SearchResponse object
			TermsFacet f = (TermsFacet) response.getFacets().facetsAsMap()
					.get("subjectFacet");
			long total = f.getTotalCount(); // Total terms doc count
			long other = f.getOtherCount(); // Not shown terms doc count
			long miss = f.getMissingCount(); // Without term doc count
			LOG.info("total:" + total + ",  other:" + other + ",  miss:" + miss);
			TermFacetOutput termFacetOutModule = new TermFacetOutput(total, other,
					miss);
			// For each entry
			int i = 0;
			for (TermsFacet.Entry entry : f) {
				String term = entry.getTerm().toString(); // Term
				int count = entry.getCount(); // Doc count
				termFacetOutModule.addTCItem(new TermFacetOutput.TermCount(term,
						count));
				LOG.info("Entry[" + i + "]  term:" + term + ",  count:" + count);
				i++;
			}

			return termFacetOutModule;
		}else{
			//ES检索失败--term元素统个数计失败
			return null;
		}

	}
	
	public String getUserTrace(String probe){
		PhnUserTrace userTrace = new PhnUserTrace();
//		FilterBuilder imsifilter = FilterBuilders.termFilter("imsi", targetImsi);
		FilterBuilder probefilter = FilterBuilders.termFilter("probeId", probe);
		SearchResponse response = client.prepareSearch(indexName)
				.setTypes(idxtypeName)
				.setSearchType(SearchType.QUERY_AND_FETCH)  
				.setSize(100)
				.setPostFilter(probefilter)
				.execute().actionGet();
		
	    SearchHits hits = response.getHits();  
	    long recordnum = hits.getTotalHits();
	    userTrace.setTotalhits(recordnum);
	    
	    for (SearchHit hit : hits) {  
	    	Map<String, Object> record = hit.sourceAsMap();
	    	//当前用户测试轨迹
	    	userTrace.addRecord(record);
	    }
	    String ret = jsonInstance.Obj2String(userTrace);
	    return ret;
	}
	
	public String getUserNearbyTrace(String probe) throws IOException, JsonGenerationException{
		FilterBuilder imsifilter = FilterBuilders.termFilter("probeId", probe);
		SearchResponse response = client.prepareSearch(indexName)
				.setTypes(idxtypeName)
				.setSearchType(SearchType.QUERY_AND_FETCH).setSize(100)
				.setPostFilter(imsifilter).execute().actionGet();

		SearchHits hits = response.getHits();
		long recordnum = hits.getTotalHits();

		outputGenerator.writeStartArray();
		for (SearchHit hit : hits) {
			Map<String, Object> record = hit.sourceAsMap();
			String curId = hit.getId();

			if (!hashSet.contains(curId)) {
				PhnFriendsTrace oneUsrTrace = new PhnFriendsTrace();
				oneUsrTrace.setCurrent_record(record);

				// 当前用户相同imsi的进行去重处理
				Map<String, Object> maps = hit.sourceAsMap();
				if (maps.containsKey("baseposition")) {
					double latitude = 0;
					double longitude = 0;
					Map<String, Object> geohash = (Map<String, Object>) maps.get("baseposition");
					if (geohash.containsKey("lat")) {
						latitude = (Double) geohash.get("lat");
					}
					if (geohash.containsKey("lon")) {
						longitude = (Double) geohash.get("lon");
					}
					getNearbyTrace(probe, curId, latitude, longitude, oneUsrTrace);
				}
//				else if (maps.containsKey("lac")&& maps.containsKey("cellid")) {
//					int lac = (Integer) maps.get("lac");
//					int cellid = (Integer) maps.get("cellid");
//					LOG.info("(lac:"+ lac+ ", cellid:"+ cellid+ "), gps info not exist! get records in this base!");
//					getNearbyTrace(targetImsi, curId, lac, cellid, oneUsrTrace);
//				}
				else if (maps.containsKey("bs")) {
					if(maps.get("bs") instanceof Map){
						Map<String, Integer> bsMap = (Map<String, Integer>)maps.get("bs");
						int a = (Integer) bsMap.get("a");
						int b = (Integer) bsMap.get("b");
						int c = (Integer) bsMap.get("c");
						LOG.info("(lac:"+ b+ ", cellid:"+ c+ "), gps info not exist! get records in this base!");
						getNearbyTrace(probe, curId, a, b, c, oneUsrTrace);
					}
				}
				else {
					LOG.error("No (lac, cellid) info!!!");
				}

				// outputGenerator.writeObject(fndTraces[pos]);
				outputGenerator.writeRawValue(jsonInstance.Obj2String(oneUsrTrace));

			}

		}

		outputGenerator.writeEndArray();
		return getJsonString();
	}
	
	
	public void getNearbyTrace(String probe, String Cid, double lat, double lon, PhnFriendsTrace trace){
		FilterBuilder geofiter = FilterBuilders.geoDistanceFilter("baseposition")
	    .point(lat, lon)
	    .distance(1000, DistanceUnit.METERS)
	    .optimizeBbox("memory")                    // Can be also "indexed" or "none"
	    .geoDistance(GeoDistance.ARC);            // Or GeoDistance.PLANE
		
	    SearchResponse response = client.prepareSearch(indexName)  
	            .setTypes(idxtypeName)  
	            .setSearchType(SearchType.DFS_QUERY_AND_FETCH)  
	            .setPostFilter(geofiter)   // Filter  
	            .setFrom(0).setSize(100).setExplain(true)  
	            .execute()  
	            .actionGet();  
	      
	    SearchHits hits = response.getHits();  
	    trace.setNearyby_number(hits.getTotalHits()-1);

	    
	    for (SearchHit hit : hits) {
	    	String Nid = hit.getId();
	    	
	    	//将当前用户测试记录从周围朋友中删除
	    	if(!Cid.equals(Nid)){
//	    		String nearImsi;
//	    		int nearpid;
	    		Map<String, Object> nearMap = hit.sourceAsMap();
	    		trace.addNearby(nearMap);
	    		//相同用户IMSI形同
//	    		if(nearMap.containsKey("imsi")){
//	    			nearImsi = (String)nearMap.get("imsi");
//	    			if(nearImsi.equals(imsi)){
//	    				hashSet.add(Nid);
//	    			}
//	    		}
	    		//相同用户IMSI形同
	    		if(nearMap.containsKey("probeId")){
//	    			nearImsi = (String)nearMap.get("probeId");
	    			int nearpid = (Integer)nearMap.get("probeId");
	    			int probeid = Integer.parseInt(probe);
	    			if(nearpid == probeid){
	    				hashSet.add(Nid);
	    			}
	    		}
//    			hashSet.add(Nid);
	    	}
	    } 
	}
	
	private void getNearbyTrace(String probe, String Cid, int mnc, int lac, int cellid, PhnFriendsTrace trace){
		FilterBuilder mncFilter = FilterBuilders.termFilter("bs.a", mnc);
		FilterBuilder lacFilter = FilterBuilders.termFilter("bs.b", lac);
		FilterBuilder cellFilter = FilterBuilders.termFilter("bs.c", cellid);
		AndFilterBuilder cgifilter = FilterBuilders.andFilter(mncFilter,lacFilter,cellFilter);
		
		SearchResponse response = client.prepareSearch(indexName)
				.setTypes(idxtypeName)
				.setSearchType(SearchType.QUERY_AND_FETCH)  
				.setSize(100)
				.setPostFilter(cgifilter)
				.execute().actionGet();
		
	    SearchHits hits = response.getHits();
	    trace.setNearyby_number(hits.getTotalHits()-1);
	     
//	    for (SearchHit hit : hits) {  
//	    	trace.addNearby(hit.sourceAsMap());
//	    }
	    
	    for (SearchHit hit : hits) {
	    	String Nid = hit.getId();
	    	//将当前用户测试记录从周围朋友中删除
	    	if(!Cid.equals(Nid)){
//	    		String nearImsi;
	    		Map<String, Object> nearMap = hit.sourceAsMap();
	    		trace.addNearby(nearMap);
	    		//相同用户IMSI形同
//	    		if(nearMap.containsKey("imsi")){
//	    			nearImsi = (String)nearMap.get("imsi");
//	    			if(nearImsi.equals(imsi)){
//	    				hashSet.add(Nid);
//	    			}
//	    		}
	    		if(nearMap.containsKey("probeId")){
//	    			nearImsi = (String)nearMap.get("probeId");
//	    			if(nearImsi.equals(probe)){
//	    				hashSet.add(Nid);
//	    			}
	    			int nearpid = (Integer)nearMap.get("probeId");
	    			int probeid = Integer.parseInt(probe);
	    			if(nearpid == probeid){
	    				hashSet.add(Nid);
	    			}
	    		}
	    	}
	    } 
	}
	
	
	private String getJsonString() {
		hashSet = new HashSet<String>();
		try {
			outputGenerator.flush();
			out.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		String res = out.toString();
		out.reset();
		return res;
	}
	
//	private PhoneHistory getPhoneHistory(int phoneid, int hsize, int hnum){
//	String[] historyItem = new String[hsize];
//	QueryBuilder phoneHistoryQ = QueryBuilders
//            .boolQuery()
//            .must(QueryBuilders.termQuery("probetype", 2))
//            .must(QueryBuilders.termQuery("probeid", phoneid));
//	
//	SearchResponse response = client.prepareSearch(indexName)
//	        .setTypes(idxtypeName)
//	        .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
//	        .setQuery(phoneHistoryQ)             // Query
//	        .setFrom((hnum-1)*hsize).setSize(hnum*hsize).setExplain(true)
//	        .execute()
//	        .actionGet();
//	
//	if(response.status() == RestStatus.OK){
//	    SearchHits hits = response.getHits();
//	    int count=0;
//	    for (SearchHit hit : hits) {  
//	    	historyItem[count++] = hit.getSourceAsString();  
//	    }
//	    
//	    PhoneHistory historyRecode = new PhoneHistory(count);
//	    for(int i=0; i<count; i++){
//	    	historyRecode.addItem(historyItem[i]);
//	    }
//		return historyRecode;
//	}else{
//		return null;
//	}
//
//}
}
